﻿Imports System.Net
Imports System.Net.Sockets
Imports System.Threading

Public Class clsWinSock
    Implements IDisposable
#Region " IDisposable Support "
    Private disposedValue As Boolean = False        ' 偵測多餘的呼叫
    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' 關閉sock連線
                mySockClose()
            End If
            ' TODO: 釋放共用的 Unmanaged 資源
        End If
        Me.disposedValue = True
    End Sub
    ' 由 Visual Basic 新增此程式碼以正確實作可處置的模式。
    Public Sub Dispose() Implements IDisposable.Dispose
        ' 請勿變更此程式碼。在以上的 Dispose 置入清除程式碼 (ByVal 視為布林值處置)。
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

#Region "<事件>"
    ' SOCK 已斷線
    Public Event SckClose(ByVal sender As clsWinSock, ByVal IP As String, ByVal PORT As Integer, ByVal RemoteBreak As Boolean)
    ' SOCK 已連線
    Public Event SckConnect(ByVal sender As clsWinSock, ByVal IP As String, ByVal PORT As Integer)
    ' SOCK 連線要求
    Public Event SckConnectionRequest(ByVal sender As clsWinSock)
    ' SOCK Data 
    Public Event SckDataArrival(ByVal sender As clsWinSock, ByVal Data() As Byte)
    ' SOCK SendComplete
    Public Event SckSendComplete(ByVal sender As clsWinSock, ByVal Count As Integer)
    ' SOCK Error
    Public Event SckError(ByVal sender As clsWinSock, ByVal Description As String)
#End Region

#Region "<SOCKET>"
    Private mSock As TcpClient
    Private mRemoteIP As String = "127.0.0.1"
    Private mRemotePORT As Integer = 0

    ' 連線
    Public Sub Connect(ByVal IP As String, ByVal PORT As Integer)
        Try
            Me.mRemoteIP = Dns.GetHostAddresses(IP)(0).ToString
            Me.mRemotePORT = PORT

            '新連線
            Dim tEndPoint As New IPEndPoint(IPAddress.Parse(mRemoteIP), mRemotePORT)
            Me.mSock.Client.BeginConnect(tEndPoint, New AsyncCallback(AddressOf InvokeConnect), Me.mSock)

        Catch ex As Exception
            ' Sock Error
            RaiseEvent SckError(Me, ex.Message)
            ' 斷線事件
            RaiseSckClose(False)
        End Try
    End Sub
    ' 等待連線
    Private Sub InvokeConnect(ByVal ar As IAsyncResult)
        Dim IsConnect As Boolean = False
        Try
            ' 如果已連線
            If IsNothing(Me.mSock) = False AndAlso Me.mSock.Client.Connected = True Then
                ' 建立收集資料執行緒
                Dim myBuff(Me.mSock.Client.SendBufferSize - 1) As Byte
                Me.mSock.Client.BeginReceive(myBuff, 0, myBuff.Length, SocketFlags.None, AddressOf ReceiveCallBack, myBuff)
                IsConnect = True
            End If

        Catch ex As Exception
            ' Sock Error
            RaiseEvent SckError(Me, ex.Message)
            ' 斷線事件
            RaiseSckClose(False)
            Exit Sub
        End Try

        ' 產生已連線事件
        If IsConnect Then
            RaiseEvent SckConnect(Me, Me.mRemoteIP, Me.mRemotePORT)
        Else
            ' 斷線事件
            RaiseSckClose(False)
        End If
    End Sub
    ' 非同步收集資料執行緒
    Private Sub ReceiveCallBack(ByVal ar As IAsyncResult)
        If IsNothing(mSock) Then Exit Sub

        '--收到網路封包
        Dim bytes() As Byte = ar.AsyncState
        '--檢查網路封包
        Dim numBytes As Int32

        Try
            numBytes = mSock.Client.EndReceive(ar)
        Catch ex As Exception
            ' Sock Error
            RaiseEvent SckError(Me, ex.Message)
            ' 斷線事件
            RaiseSckClose(False)
            ' 離開
            Exit Sub
        End Try

        ' 如果Sock物件已經不存在，離開
        If IsNothing(mSock) Then Exit Sub

        ' 連線狀態
        If IsNothing(Me.mSock) = False AndAlso Me.mSock.Client.Connected Then
            ' 如果收到SockClose
            If mSock.Client.Poll(1, SelectMode.SelectRead) And numBytes = 0 Then
                ' 斷線事件
                RaiseSckClose(True)
                ' 離開
                Exit Sub

            Else
                ' 如果資料大於0
                If numBytes > 0 Then
                    Dim tmpBuff(numBytes - 1) As Byte
                    Array.Copy(bytes, 0, tmpBuff, 0, tmpBuff.Length)
                    RaiseEvent SckDataArrival(Me, tmpBuff)
                End If

                Try
                    If IsNothing(mSock) Then Exit Sub
                    ' 繼續ReceiveCallBack
                    mSock.Client.BeginReceive(bytes, 0, bytes.Length, SocketFlags.None, AddressOf ReceiveCallBack, bytes)
                Catch ex As Exception
                    ' Sock Error
                    RaiseEvent SckError(Me, ex.Message)
                    ' 斷線事件
                    RaiseSckClose(False)
                End Try
            End If ' 如果收到SockClose
        End If
    End Sub
    ' 斷線
    Public Sub DisConnect()
        ' 關閉sock連線
        mySockClose()
    End Sub
    ' 斷線事件
    Private Sub RaiseSckClose(ByVal RemoteBreak As Boolean)
        ' 關閉sock連線
        mySockClose()
        RaiseEvent SckClose(Me, Me.mRemoteIP, Me.mRemotePORT, RemoteBreak)
    End Sub
    ' 關閉sock連線
    Private Sub mySockClose()
        ' 如果已連線，釋放連線
        If IsNothing(Me.mSock) = False Then
            ' 關閉連線
            If Me.mSock.Client.Connected Then Me.mSock.Client.Shutdown(SocketShutdown.Both)
            Me.mSock.Client.Close()
            ' 釋放連線
            Me.mSock = Nothing
        End If
    End Sub
#End Region

#Region " [SOCK SEND] "
    Public Sub Send(ByVal Data As Byte())
        If IsNothing(Me.mSock) = False AndAlso Me.mSock.Client.Connected Then
            Me.mSock.Client.BeginSend(Data, 0, Data.Length, SocketFlags.None, New AsyncCallback(AddressOf SendCallBack), Me.mSock)
        End If
    End Sub
    Private Sub SendCallBack(ByVal ar As IAsyncResult)
        Dim SendCount As Integer = -1
        Try
            Me.mSock = ar.AsyncState
            SendCount = Me.mSock.Client.EndSend(ar)

        Catch ex As Exception
            ' Sock Error
            RaiseEvent SckError(Me, ex.Message)
            ' 斷線事件
            RaiseSckClose(False)
        End Try

        If SendCount <> -1 Then
            RaiseEvent SckSendComplete(Me, SendCount)
        End If
    End Sub
#End Region

#Region " 屬性 "
    ' 連線狀態
    ReadOnly Property Connected() As Boolean
        Get
            Return IsNothing(Me.mSock) = False AndAlso Me.mSock.Client.Connected
        End Get
    End Property
    ' IP
    ReadOnly Property IP() As String
        Get
            Return Me.mRemoteIP
        End Get
    End Property
    ' PORT
    ReadOnly Property PORT() As Integer
        Get
            Return Me.mRemotePORT
        End Get
    End Property
#End Region

    Public Sub New()
        Me.mSock = New TcpClient
    End Sub
End Class





'' Created by TheUberOverLord as an example using VB.NET and Skype4COM and ports

'' Simply create a VB.NET form project and delete the entire contents of Form1.vb
'' Add these contents and right click on Your VB project solution and add a reference
'' using the COM tab to Skype4COM, build then debug. There are many MsgBox's that can
'' be uncommented to help you test.

'Imports System
'Imports System.Net
'Imports System.Net.Sockets
'Imports System.Threading
'Imports System.Text
'Imports System.IO

'Public Class Form1

'    ' Set to true if we are connected to a port
'    Dim Connected = False

'    ' This will work with IPv4 and IPv6
'    Dim localEndPoint As New IPEndPoint(System.Net.IPAddress.Loopback, 11000)

'    ' Define our listener Socket
'    Dim listener As New Socket(localEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp)

'    Dim handler As Socket

'    ' We want to use Skype with event handlers so we do this
'    Public WithEvents oSkype As New SKYPE4COMLib.Skype

'    Public Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load

'        ' Always use Try/Catch logic, it's just good coding practice
'        Try

'            ' Bind the socket to the local endpoint
'            listener.Bind(localEndPoint)

'            ' Only allow one opener on the port we are going to open and listen on
'            listener.Listen(0)

'            ' Define our Skype interface
'            oSkype = New SKYPE4COMLib.Skype

'            ' Start Skype if Needed
'            If oSkype.Client.IsRunning = False Then
'                oSkype.Client.Start(True)
'            End If

'            ' Connect to Skype and don't wait for the Attachment to complete
'            ' We are monitoring the AttachmentStatus Event we will know
'            ' When our attachment request has completed successfully
'            oSkype.Attach(8, False)

'        Catch ex As Exception
'            MsgBox("Something in Form1_Load Failed: " + ex.ToString, MsgBoxStyle.Information)
'        End Try

'    End Sub

'    Public Sub oSkype_AttachmentStatus(ByVal Status As SKYPE4COMLib.TAttachmentStatus) Handles oSkype.AttachmentStatus

'        ' Always use Try/Catch logic, it's just good coding practice
'        Try

'            ' Uncomment this below to see all Attachment status messages
'            'MsgBox("oSkype_AttachmentStatus: Attachment Status: " + oSkype.Convert.AttachmentStatusToText(Status))

'            If Status = SKYPE4COMLib.TAttachmentStatus.apiAttachSuccess Then

'                ' Always use Try/Catch logic, it's just good coding practice
'                Try

'                    ' Uncomment this below to see just Attachment status messages
'                    'MsgBox("oSkype_AttachmentStatus: We are now Attached: " + oSkype.Convert.AttachmentStatusToText(Status))

'                    ' Launch ALL Mainline Logic here
'                    ' Example for PSTN calls: oSkype.PlaceCall(Target:="+16513404792")
'                    ' We are going to create a call for echo123 so that when any input is
'                    ' sent, it will be recorded and played back

'                    ' NOTE: We can't make a call if our user status is Offline
'                    If oSkype.CurrentUserStatus <> SKYPE4COMLib.TUserStatus.cusOffline Then
'                        oSkype.PlaceCall(Target:="echo123")
'                    End If

'                Catch ex As Exception
'                    MsgBox("oSkype_AttachmentStatus: Call Failed To Echo123: " + ex.ToString, MsgBoxStyle.Information)
'                End Try

'            End If

'            If Status = SKYPE4COMLib.TAttachmentStatus.apiAttachAvailable Then

'                ' Always use Try/Catch logic, it's just good coding practice
'                Try

'                    ' Uncomment this below to see just Re-Attachment status messages
'                    'MsgBox("oSkype_AttachmentStatus: We are now Re-Attached: " + oSkype.Convert.AttachmentStatusToText(Status))

'                    ' Something happened and we need to re-attach
'                    ' and not wait for the attachment to complete
'                    ' Our mainline logic will start again when we receive
'                    ' an Attach Status of success
'                    oSkype.Attach(8, False)

'                Catch ex As Exception
'                    MsgBox("oSkype_AttachmentStatus: Re-Attach Problem: " + ex.ToString, MsgBoxStyle.Information)
'                End Try

'            End If

'        Catch ex As Exception
'            MsgBox("Something in oSkype_AttachmentStatus Failed: " + ex.ToString, MsgBoxStyle.Information)
'        End Try

'    End Sub

'    ' This event will fire when any call status change takes place
'    ' We have no need to monitor any call status other than a call in progress
'    ' so that we can begin to accept an open on our port by the Skype client
'    Public Sub oSkype_CallStatus(ByVal pCall As SKYPE4COMLib.ICall, ByVal Status As SKYPE4COMLib.TCallStatus) Handles oSkype.CallStatus

'        ' Always use Try/Catch logic, it's just good coding practice
'        Try

'            ' Uncomment the line below to display any call status in a message box
'            'MsgBox("oSkype_CallStatus: " + pCall.PartnerHandle + " " + oSkype.Convert.CallStatusToText(Status))

'            ' Only do this if we have a call in progress and if we are already
'            ' doing this for another call, don't do this for any other calls
'            If Status = SKYPE4COMLib.TCallStatus.clsInProgress And Connected = False Then

'                ' Start listening for a connection from the Skype client
'                ' We do this for each call because we shutdown and close the handler
'                ' each time a call completes in the ReadCallback sub
'                listener.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), listener)

'                ' Uncomment "one" of the below, this example
'                ' supports all possible interfaces Input, Output, Microphone using ports

'                ' Tell the Skype client we want to send input data on port 11000
'                pCall.InputDevice(SKYPE4COMLib.TCallIoDeviceType.callIoDeviceTypePort) = 11000
'                ' Tell the Skype client we want to read output data on port 11000
'                'pCall.OutputDevice(SKYPE4COMLib.TCallIoDeviceType.callIoDeviceTypePort) = 11000
'                ' Tell the Skype client we want to read microphone data on port 11000
'                'pCall.CaptureMicDevice(SKYPE4COMLib.TCallIoDeviceType.callIoDeviceTypePort) = 11000

'                ' Uncomment the line below to display only the InProgress call status in a message box
'                'MsgBox("oSkype_CallStatus: " + pCall.PartnerHandle + " " + oSkype.Convert.CallStatusToText(Status))

'            End If

'        Catch ex As Exception
'            MsgBox("Something in oSkype_CallStatus Failed: " + ex.ToString, MsgBoxStyle.Information)
'        End Try

'    End Sub

'    Private Sub oSkype_CallInputStatusChanged(ByVal pCall As SKYPE4COMLib.ICall, ByVal Status As Boolean) Handles oSkype.CallInputStatusChanged

'        ' Always use Try/Catch logic, it's just good coding practice
'        Try
'            ' Uncomment the line below to display CallInput Status Changes
'            'MsgBox("Call Input Status Changed: " + Status.ToString())

'        Catch ex As Exception
'            MsgBox("Something in oSkype_CallInputStatusChanged Failed: " + ex.ToString, MsgBoxStyle.Information)
'        End Try

'    End Sub

'    ' When this Skype client opens port 11000 this event fires
'    Public Sub AcceptCallback(ByVal ar As IAsyncResult)

'        ' Always use Try/Catch logic, it's just good coding practice
'        Try

'            ' Uncomment the line below to display when the Skype client opens port 11000
'            'MsgBox("AcceptCallback: The Skype client has connected on port 11000")

'            Connected = True

'            ' Get the socket that handles the client request.
'            listener = CType(ar.AsyncState, Socket)

'            ' End the operation.
'            handler = listener.EndAccept(ar)

'            ' Create the state object for the async receive.
'            Dim state As New StateObject
'            state.workSocket = handler

'            ' When a call completes, we will get a read on port 11000 of zero length
'            ' in the ReadCallback sub once we set it here, so we want to prepare to
'            ' receive that since we have a good connection with the Skype client
'            ' for this call
'            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)

'        Catch ex As Exception
'            MsgBox("Something in AcceptCallback Failed: " + ex.ToString, MsgBoxStyle.Information)
'        End Try

'    End Sub 'AcceptCallback


'    Public Sub ReadCallback(ByVal ar As IAsyncResult)

'        ' Always use Try/Catch logic, it's just good coding practice
'        Try

'            ' Retrieve the state object and the handler socket
'            ' from the asynchronous state object.
'            Dim state As StateObject = CType(ar.AsyncState, StateObject)
'            handler = state.workSocket

'            ' Never used because in this example we never received data
'            Dim content As String = String.Empty

'            ' Read data from the client socket. 
'            Dim bytesRead As Integer = handler.EndReceive(ar)

'            ' Uncomment the line below to display When a call completed
'            ' or when data was received for a call
'            'MsgBox("ReadCallback: bytesRead = " + bytesRead.ToString())

'            ' bytesRead will never be > 0 if this connection is sending
'            ' data into a call using input. However, it can be > 0 when receiving
'            ' data from a call for output or microphone
'            If bytesRead > 0 Then

'                ' This must be set to receive data for output or microphone
'                ' Your logic here to process the data

'                ' Keep getting data as long as the call is in progress
'                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReadCallback), state)

'            Else

'                ' A Skype call has ended so we want to shutdown and close our handler
'                If handler.Connected Then
'                    handler.Shutdown(SocketShutdown.Both)
'                    handler.Close()
'                    Connected = False
'                End If

'            End If

'        Catch ex As Exception
'            MsgBox("Something in ReadCallback Failed: " + ex.ToString, MsgBoxStyle.Information)
'        End Try

'    End Sub 'ReadCallback

'    ' This is what you would use to send data to the Skype client for port 11000
'    ' If your data is already in bytes you can skipe the conversion process
'    ' You would need to do:
'    ' Dim state As StateObject = CType(ar.AsyncState, StateObject)
'    ' Dim handler As Socket = state.workSocket
'    ' Send(handler, content)

'    Public Sub Send(ByVal handler As Socket, ByVal data As String)

'        ' Always use Try/Catch logic, it's just good coding practice
'        Try

'            ' Convert the string data to byte data
'            ' allocate byte array based on half of string length
'            Dim numBytes As Integer = (data.Length) / 2
'            Dim bytes As Byte() = New Byte(numBytes - 1) {}

'            ' loop through the string - 2 bytes at a time converting it to decimal equivalent and store in byte array
'            ' x variable used to hold byte array element position
'            For x As Integer = 0 To numBytes - 1
'                bytes(x) = Convert.ToByte(data.Substring(x * 2, 2), 16)
'            Next

'            ' Uncomment the line below to display number of bytes being sent
'            'MsgBox("Send: bytes being sent = " + bytes.Length.ToString())

'            ' Begin sending the data to the remote device.
'            handler.BeginSend(bytes, 0, bytes.Length, 0, New AsyncCallback(AddressOf SendCallback), handler)

'        Catch ex As Exception
'            MsgBox("Something in Send Failed: " + ex.ToString, MsgBoxStyle.Information)
'        End Try

'    End Sub 'Send


'    Public Sub SendCallback(ByVal ar As IAsyncResult)

'        ' Always use Try/Catch logic, it's just good coding practice
'        Try

'            ' Retrieve the socket from the state object.
'            handler = CType(ar.AsyncState, Socket)

'            ' Complete sending the data to the remote device.
'            Dim bytesSent As Integer = handler.EndSend(ar)

'            ' Uncomment this to see how many bytes were sent each time.
'            'MsgBox("We sent: " + bytesSent.ToString())

'        Catch ex As Exception
'            MsgBox("Something in SendCallback Failed: " + ex.ToString, MsgBoxStyle.Information)
'        End Try

'    End Sub 'SendCallback

'    ' State object for reading client data asynchronously
'    Public Class StateObject
'        ' Client  socket.
'        Public workSocket As Socket = Nothing
'        ' Size of receive buffer. NOTE: This could be larger. change and test
'        Public Const BufferSize As Integer = 8192
'        ' Receive buffer.
'        Public buffer(BufferSize) As Byte
'        ' Received data string.
'        Public sb As New StringBuilder
'    End Class 'StateObject

'End Class






